package stream

type Stream interface {
	ReadRange(uint32, uint32) []byte
	Len() uint32
}

type StreamMarker struct {
	Start        uint32
	Stop         uint32
	Mark         uint32
	IsCompressed bool
}

type MultiPartStream struct {
	Buffer  []byte
	Markers []StreamMarker
	Size    uint32
}

func bsearch(n uint32, f func(uint32) bool) uint32 {
	i, j := uint32(0), n
	for i < j {
		h := (i + j) >> 1
		if !f(h) {
			i = h + 1
		} else {
			j = h
		}
	}
	return i
}

// ReadRangeBuild(a, ReadInt32(120), ReadInt32(124))

func (s *MultiPartStream) ReadRangeMake() {}

func (stream *MultiPartStream) ReadRange(start, stop uint32) []byte {
	// bsearch index
	n := uint32(len(stream.Markers))
	start_idx := bsearch(n, func(i uint32) bool { return stream.Markers[i].Mark > start })
	stop_idx := bsearch(n, func(i uint32) bool { return stream.Markers[i].Mark >= stop })
	if start_idx == n {
		return nil
	}
	start_off := start - stream.Markers[start_idx].Mark + stream.Markers[start_idx].Stop
	stop_off := stop - stream.Markers[stop_idx].Mark + stream.Markers[stop_idx].Stop
	ret := make([]byte, stop-start)

	// copy start block
	p := copy(ret, stream.Buffer[start_off:stream.Markers[start_idx].Stop])

	if start_idx == stop_idx {
		return ret
	}

	// copy inter block
	for _, marker := range stream.Markers[start_idx+1 : stop_idx] {
		p += copy(ret[p:], stream.Buffer[marker.Start:marker.Stop])
	}

	if stop_idx == n {
		return ret
	}
	// copy stop block
	copy(ret[p:], stream.Buffer[stream.Markers[stop_idx].Start:stop_off])
	return ret
}

func (stream *MultiPartStream) Len() uint32 {
	return stream.Size
}

func New(buffer []byte, chain []uint32, multiplier uint32, size uint32) Stream {
	markers := make([]StreamMarker, 0, len(chain))
	mark := multiplier
	p := -1
	n := len(chain)
	q := 0
	for q < n {
		if q > 0 && chain[q] == chain[q-1]+1 {
			markers[len(markers)-1].Stop += multiplier
			markers[len(markers)-1].Mark = mark
		} else {
			p++
			marker := StreamMarker{
				Start: chain[q] * multiplier,
				Stop:  chain[q]*multiplier + multiplier,
				Mark:  mark,
			}
			markers = append(markers, marker)
		}
		mark += multiplier
		q++
	}
	return &MultiPartStream{
		Buffer:  buffer,
		Markers: markers,
		Size:    size,
	}
}

func NewPiece() {}
